Tape = function (alphabet) {
    this._symbols = {};
    this._alphabet = alphabet;
    this._firstNotDefaultCharIndex = Number.POSITIVE_INFINITY;
    this._lastNotDefaultCharIndex = Number.NEGATIVE_INFINITY;
};

Tape.prototype = {
    getSymbol:function (index) {
        var symbol = this._symbols[index.toString()];
        if (typeof(symbol) === 'undefined')
            symbol = this._alphabet.getDefaultSymbol();
        return symbol;
    },

    setTapeClean:function() {
        for (var i in this._symbols) {
            delete this._symbols[i];
        }
        this._firstNotDefaultCharIndex = Number.POSITIVE_INFINITY;
        this._lastNotDefaultCharIndex = Number.NEGATIVE_INFINITY;
    },

    findMaxIndex:function() {
        var maxIndex = Number.NEGATIVE_INFINITY;
        for (var idx in this._symbols) {
            if (idx * 1.0 > maxIndex) {
                maxIndex = idx;
            }
        }
        return maxIndex;
    },

    findMinIndex:function() {
        var minIndex = Number.POSITIVE_INFINITY;
        for (var idx in this._symbols) {
            if (idx * 1.0 < minIndex) {
                minIndex = idx * 1.0;
            }
        }
        return minIndex;
    },

    // deletes the symbol at index and maintains first and last not default characters properly.
    deleteSymbol:function (index) {
        delete this._symbols[index.toString()];
        if (index == this._firstNotDefaultCharIndex) {
            this._firstNotDefaultCharIndex = this.findMinIndex();
        } else if (index == this._lastNotDefaultCharIndex) {
            this._lastNotDefaultCharIndex = this.findMaxIndex();
        }
        if (this._firstNotDefaultCharIndex == Number.POSITIVE_INFINITY
         || this._lastNotDefaultCharIndex == Number.NEGATIVE_INFINITY) {
            this.setTapeClean();
        }
    },

    setSymbol:function (index, symbol) {
        if (!(this._alphabet.containsSymbol(symbol))) {
            alert('wrong symbol at Tape.setSymbol');
            return;
        }
        index = index * 1.0;
        if (this._alphabet.getDefaultSymbol() == symbol) {
            if ((index == this._firstNotDefaultCharIndex || index == this._lastNotDefaultCharIndex)) {
                this.deleteSymbol(index);
            }
            return;
        }
        this._symbols[index.toString()] = symbol;
        this._firstNotDefaultCharIndex = index * 1.0 < this._firstNotDefaultCharIndex * 1.0 ? index : this._firstNotDefaultCharIndex;
        this._lastNotDefaultCharIndex = index * 1.0 > this._lastNotDefaultCharIndex * 1.0 ? index : this._lastNotDefaultCharIndex;
    },

    getTapeRange:function (beginIndex, endIndex) {
        var ret = new Array();
        for (var i = beginIndex * 1.0; i < endIndex * 1.0; ++i) {
            ret.push(this.getSymbol(i));
        }
        return ret;
    },

    getAlphabet:function () {
        return this._alphabet;
    },

    getFirstNotDefaultCharIndex:function () {
        return this._firstNotDefaultCharIndex;
    },

    getLastNotDefaultCharIndex:function () {
        return this._lastNotDefaultCharIndex;
    },

    fillTape:function (symbols, beginIndex) {
        for (var i = 0; i < symbols.length; ++i) {
            this.setSymbol(i + beginIndex, symbols[i]);
        }
    },

    isClean:function () {
        return this._firstNotDefaultCharIndex == Number.POSITIVE_INFINITY
            && this._lastNotDefaultCharIndex == Number.NEGATIVE_INFINITY;
    },

    copy:function () {
        var tapeCopy = new Tape(this._alphabet);
        for (var s in this._symbols) {
            tapeCopy.setSymbol(s, this._symbols[s]);
        }
        return tapeCopy;
    }
};
